package cc.verywell.pureblock.msg.nfs.linkmap;

import java.io.Closeable;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
* 
* Copyright © 2019 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2019年6月16日 下午2:43:18
*/
public class EasyIndex implements EasyTable,Closeable{

	private EasyTableMem memmap;
	private long memSize = 0;
	
	private EasyTableMem memsave;
	private boolean memsaveNeed = false;
	
	final private EasyFileController fc;
	
	private boolean closed = false;
	
	public EasyIndex(EasyFileController fc,final ThreadGroup tg) {
		this.fc = fc;
		this.backExe = Executors.newSingleThreadExecutor(new ThreadFactory() {
;			@Override
			public Thread newThread(Runnable r) {
				return new Thread(tg,r,"EasyIndexBackground");
			}
		});
		memmap = fc.newTableMem();
	}
	@Override
	public void close(){
		synchronized (this) {
			if(closed)return;
			closed = true;
			if(memmap!=null)memmap.close();
			if(memsave!=null)memsave.close();
		}
		backExe.shutdownNow();
		this.fc.closeFromIndex();
	}
	
	public void put(byte[] key, byte[] linkfrom, int prierty) {
		LinkCell cell = new LinkCell(key, linkfrom, prierty, this.fc.getNewSeq(), false);
		add(cell);
	}
	public void put(byte[] key) {
		LinkCell cell = new LinkCell(key, this.fc.getNewSeq(), false);
		add(cell);
	}
	public void remove(byte[] key, byte[] linkfrom, int prierty) {
		LinkCell cell = new LinkCell(key, linkfrom, prierty, this.fc.getNewSeq(), true);
		add(cell);
	}
	public void remove(byte[] key) {
		LinkCell cell = new LinkCell(key, this.fc.getNewSeq(), true);
		add(cell);
	}
	
	
	
	protected void add(LinkCell cell) {
		boolean runb;
		synchronized (this) {
			runb = checkMemsize();
			memmap.add(cell);
			memSize += cell.encodeSize();
		}
		if(runb) {
			runBackground();
		}
	}
	protected void addAll(Collection<? extends LinkCell>  cells) {
		boolean runb;
		synchronized (this) {
			runb = checkMemsize();
			memmap.addAll(cells);
			for (LinkCell cell : cells) {
				memSize += cell.encodeSize();
			}
			if(!runb) {
				runb = checkMemsize();
			}
		}
		if(runb) {
			runBackground();
		}
	}

	final private Runnable updateMemdo = new Runnable() {
		@Override
		public void run() {
			synchronized (EasyIndex.this) {
				memsave = null;
				EasyIndex.this.notifyAll();
			}
			if(checkMemsize()) {
				runBackground();
			}
		}
	};  
	private boolean checkMemsize() {
		synchronized (this) {
			if(closed)throw new EasyIOException("easyIndex Closed");
			if(memSize>EasyFileController.MEM_SAVE_SIZE) {
				if(memsave==null) {
					memsave = memmap;
					memsaveNeed = true;
					memmap = fc.newTableMem();
					memSize = 0;
				}else{
					while(memSize>=EasyFileController.MAX_MEM_SIZE) {
						try {
							this.wait();
							if(closed)throw new EasyIOException("easyIndex Closed after Wait");
						} catch (InterruptedException e) {
							throw new EasyIOException(e);
						}
					}
				}
			}
			if(memsaveNeed && fc.newMemCompact(memsave, updateMemdo)) {
				memsaveNeed = false;
				return true;
			}
			return false;
		}
	}
	private ExecutorService backExe;
	private boolean running = false;
	private Runnable runCompact = new Runnable() {
		@Override
		public void run() {
			boolean successOnce = false;
			try {
				if(fc.runMemCompact())successOnce = true;
				if(fc.runGradeCompact())successOnce = true;
			}finally {
				synchronized (EasyIndex.this) {
					running = false;
					if(successOnce)runBackground();
				}
			}
		}
	};
	private void runBackground() {
		synchronized (this) {
			if(closed)throw new EasyIOException("easyIndex Closed");
			if(running)return;
			running = true;
			backExe.execute(runCompact);
		}
	}
	
	
	@Override
	public EasyIterator get(LinkCell startWith) {
		EasyIterator memit,saveit;
		EasyFileController fc;
		
		synchronized (this) {
			if(closed)throw new EasyIOException("easyIndex Closed");
			memit = memmap!=null?memmap.get(startWith):null;
			saveit = memsave!=null?memsave.get(startWith):null;
			fc = this.fc;
		}
		
		EasyIteratorGroup g = null;
		if(memit!=null) {
			if(g==null)g = new EasyIteratorGroup();
			g.add(memit);
		}
		if(saveit!=null) {
			if(g==null)g = new EasyIteratorGroup();
			g.add(saveit);
		}
		if(fc!=null) {
			g = fc.getTableGroup().get(g, startWith);
		}
		if(g!=null) {
			return new EasyIteratorNoRemove(g);
		}
		return null;
	}
	public boolean has(LinkCell startWith) {
		synchronized (this) {
			if(memmap!=null) {
				EasyIterator it = memmap.get(startWith);
				if(it!=null) {
					while(it.hasNext()) {
						if(!it.next().isRemove())return true;
					}
				}
			}
		}
		EasyIterator it = get(startWith);
		return it!=null && it.hasNext();
	}
	
	
	
	
}
